﻿using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;
using System;

namespace Blockey.GameObjects3D
{
    public class Goal3D : DrawableModel
    {
        public Goal3D(GraphicsDevice oGraphics, ContentManager oManager, bool bIsAway)
            : base(oGraphics, oManager, "Models\\Goal\\Goal", true)
        {
            if (bIsAway == false)
            {
                var oFrontPositionX = -50.25f;

                SetTransformation(new Vector3(oFrontPositionX, 0, 0), new Vector3(MathHelper.PiOver2, 0, 0), 4.5f);

                m_oCollisionArea = new BoundingBox(
                    new Vector3(oFrontPositionX - 3.75f, -7.5f, 0),
                    new Vector3(oFrontPositionX, 7.5f, 5.4f));

                m_oScoreArea = new BoundingBox(
                    new Vector3(m_oCollisionArea.Min.X + 2.5f, m_oCollisionArea.Min.Y + 0.4f, m_oCollisionArea.Min.Z),
                    new Vector3(m_oCollisionArea.Max.X, m_oCollisionArea.Max.Y - 0.4f, m_oCollisionArea.Max.Z - 0.2f));

                m_oTopPlane = new Plane(Vector3.UnitZ, -m_oCollisionArea.Max.Z);
                m_oBackPlane = new Plane(Vector3.UnitX, -m_oCollisionArea.Min.X);
                m_oLeftPlane = new Plane(Vector3.UnitY, m_oCollisionArea.Min.Y);
                m_oRightPlane = new Plane(Vector3.UnitY, m_oCollisionArea.Max.Y);

                var oBuffer = 0.125f;
                m_oLeftPost = new BoundingBox(
                    new Vector3(m_oCollisionArea.Max.X - oBuffer, m_oCollisionArea.Max.Y - oBuffer, m_oCollisionArea.Min.Z),
                    new Vector3(m_oCollisionArea.Max.X + oBuffer, m_oCollisionArea.Max.Y + oBuffer, m_oCollisionArea.Max.Z));
                m_oRightPost = new BoundingBox(
                    new Vector3(m_oCollisionArea.Max.X - oBuffer, m_oCollisionArea.Min.Y - oBuffer, m_oCollisionArea.Min.Z),
                    new Vector3(m_oCollisionArea.Max.X + oBuffer, m_oCollisionArea.Min.Y + oBuffer, m_oCollisionArea.Max.Z));
                m_oTopPost = new BoundingBox(
                    new Vector3(m_oCollisionArea.Max.X - oBuffer, m_oCollisionArea.Min.Y - oBuffer, m_oCollisionArea.Max.Z - oBuffer),
                    new Vector3(m_oCollisionArea.Max.X + oBuffer, m_oCollisionArea.Max.Y + oBuffer, m_oCollisionArea.Max.Z + oBuffer));
            }
            else
            {

                var oFrontPositionX = 50.25f;

                SetTransformation(new Vector3(oFrontPositionX, 0, 0), new Vector3(MathHelper.PiOver2, 0, MathHelper.Pi), 4.5f);

                m_oCollisionArea = new BoundingBox(
                    new Vector3(oFrontPositionX, -7.5f, 0),
                    new Vector3(oFrontPositionX + 3.75f, 7.5f, 5.4f));

                m_oScoreArea = new BoundingBox(
                    new Vector3(m_oCollisionArea.Min.X, m_oCollisionArea.Min.Y + 0.4f, m_oCollisionArea.Min.Z),
                    new Vector3(m_oCollisionArea.Max.X - 2.5f, m_oCollisionArea.Max.Y - 0.4f, m_oCollisionArea.Max.Z - 0.2f));

                m_oTopPlane = new Plane(Vector3.UnitZ, -m_oCollisionArea.Max.Z);
                m_oBackPlane = new Plane(Vector3.UnitX, -m_oCollisionArea.Max.X);
                m_oLeftPlane = new Plane(Vector3.UnitY, m_oCollisionArea.Min.Y);
                m_oRightPlane = new Plane(Vector3.UnitY, m_oCollisionArea.Max.Y);

                var oBuffer = 0.125f;
                m_oLeftPost = new BoundingBox(
                    new Vector3(m_oCollisionArea.Min.X - oBuffer, m_oCollisionArea.Max.Y - oBuffer, m_oCollisionArea.Min.Z),
                    new Vector3(m_oCollisionArea.Min.X + oBuffer, m_oCollisionArea.Max.Y + oBuffer, m_oCollisionArea.Max.Z));
                m_oRightPost = new BoundingBox(
                    new Vector3(m_oCollisionArea.Min.X - oBuffer, m_oCollisionArea.Min.Y - oBuffer, m_oCollisionArea.Min.Z),
                    new Vector3(m_oCollisionArea.Min.X + oBuffer, m_oCollisionArea.Min.Y + oBuffer, m_oCollisionArea.Max.Z));
                m_oTopPost = new BoundingBox(
                    new Vector3(m_oCollisionArea.Min.X - oBuffer, m_oCollisionArea.Min.Y - oBuffer, m_oCollisionArea.Max.Z - oBuffer),
                    new Vector3(m_oCollisionArea.Min.X + oBuffer, m_oCollisionArea.Max.Y + oBuffer, m_oCollisionArea.Max.Z + oBuffer));
            }
        }

        public void CollisionWith(Moveable3D oMoveable)
        {
            if (oMoveable.CollidesWith(CollisionArea))
            {
                var oDelta = oMoveable.Position - oMoveable.PrevPosition;
                bool oMovingRight = oDelta.X > 0;

                var oSphere = oMoveable.BaseSphere;

                #region Posts

                if (oSphere.Intersects(PostTop) && oMovingRight)
                {
                    var oPostPosition = (PostLeft.Max + PostLeft.Min) * 0.5f;
                    oPostPosition.Y = oMoveable.PositionY;
                    var oDirection = oMoveable.Position - oPostPosition;

                    if (oDirection != Vector3.Zero)
                        oDirection.Normalize();
                    else
                        oDirection = Vector3.UnitZ;

                    oMoveable.Velocity = Vector3.Reflect(oMoveable.Velocity, oDirection);
                    oMoveable.Position = oPostPosition + (oDirection * (oMoveable.Radius + 0.5f));
                }

                if (oSphere.Intersects(PostLeft) && oMovingRight)
                {
                    var oPostPosition = (PostLeft.Max + PostLeft.Min) * 0.5f;
                    oPostPosition.Z = oMoveable.PositionZ;
                    var oDirection = oMoveable.Position - oPostPosition;
                    if (oDirection != Vector3.Zero)
                        oDirection.Normalize();
                    else
                        oDirection = Vector3.UnitZ;

                    oMoveable.Velocity = Vector3.Reflect(oMoveable.Velocity, oDirection);
                    oMoveable.Position = oPostPosition + (oDirection * (oMoveable.Radius + 0.5f));
                }
                else if (oSphere.Intersects(PostRight) && oMovingRight)
                {
                    var oPostPosition = (PostRight.Max + PostRight.Min) * 0.5f;
                    oPostPosition.Z = oMoveable.PositionZ;
                    var oDirection = oMoveable.Position - oPostPosition;
                    if (oDirection != Vector3.Zero)
                        oDirection.Normalize();
                    else
                        oDirection = Vector3.UnitZ;

                    oMoveable.Velocity = Vector3.Reflect(oMoveable.Velocity, oDirection);
                    oMoveable.Position = oPostPosition + (oDirection * (oMoveable.Radius + 0.5f));
                }
                #endregion

                #region Walls
                else
                {
                    int oSign = -Math.Sign(PlaneBack.D);

                    if (oSphere.Intersects(PlaneLeft) == PlaneIntersectionType.Intersecting)
                    {
                        if (oSign == 1)
                        {
                            if (oMoveable.PrevPosition.X < -PlaneBack.D)
                            {
                                if (oDelta.Y >= 0)
                                {
                                    oMoveable.VelocityY = -Math.Abs(oMoveable.VelocityY);
                                    oMoveable.PositionY = -PlaneLeft.D - oMoveable.Radius;
                                }
                                else
                                {
                                    oMoveable.VelocityY = Math.Abs(oMoveable.VelocityY);
                                    oMoveable.PositionY = -PlaneLeft.D + oMoveable.Radius;
                                }
                            }
                        }
                        else if (oSign == -1)
                        {
                            if (oMoveable.PrevPosition.X > -PlaneBack.D)
                            {
                                if (oDelta.Y >= 0)
                                {
                                    oMoveable.VelocityY = -Math.Abs(oMoveable.VelocityY);
                                    oMoveable.PositionY = -PlaneLeft.D - oMoveable.Radius;
                                }
                                else
                                {
                                    oMoveable.VelocityY = Math.Abs(oMoveable.VelocityY);
                                    oMoveable.PositionY = -PlaneLeft.D + oMoveable.Radius;
                                }
                            }
                        }
                    }
                    if (oSphere.Intersects(PlaneRight) == PlaneIntersectionType.Intersecting)
                    {
                        if (oSign == 1)
                        {
                            if (oMoveable.PrevPosition.X < -PlaneBack.D)
                            {
                                // Inside
                                if (oDelta.Y >= 0)
                                {
                                    oMoveable.VelocityY = -Math.Abs(oMoveable.VelocityY);
                                    oMoveable.PositionY = -PlaneRight.D - oMoveable.Radius;
                                }
                                // Outside
                                else
                                {
                                    oMoveable.VelocityY = Math.Abs(oMoveable.VelocityY);
                                    oMoveable.PositionY = -PlaneRight.D + oMoveable.Radius;
                                }
                            }
                        }
                        else if (oSign == -1)
                        {
                            if (oMoveable.PrevPosition.X > -PlaneBack.D)
                            {
                                // Inside
                                if (oDelta.Y >= 0)
                                {
                                    oMoveable.VelocityY = -Math.Abs(oMoveable.VelocityY);
                                    oMoveable.PositionY = -PlaneRight.D - oMoveable.Radius;
                                }
                                // Outside
                                else
                                {
                                    oMoveable.VelocityY = Math.Abs(oMoveable.VelocityY);
                                    oMoveable.PositionY = -PlaneRight.D + oMoveable.Radius;
                                }
                            }
                        }
                    }
                    if (oSphere.Intersects(PlaneTop) == PlaneIntersectionType.Intersecting && oMoveable is Puck3D)
                    {
                        if (oDelta.Z >= 0)
                        {
                            oMoveable.VelocityZ = -Math.Abs(oMoveable.VelocityZ);
                            if (oMoveable.PrevPosition.Z >= -PlaneTop.D)
                                oMoveable.PositionZ = -PlaneTop.D - oMoveable.Radius;
                        }
                        else
                        {
                            oMoveable.VelocityZ = Math.Abs(oMoveable.VelocityZ);
                            if (oMoveable.PrevPosition.Z < -PlaneTop.D)
                                oMoveable.PositionZ = -PlaneTop.D + oMoveable.Radius;
                        }
                    }

                    var oBufferPlane = PlaneBack;
                    oBufferPlane.D += oSign * 2.0f;
                    if (oSphere.Intersects(PlaneBack) == PlaneIntersectionType.Intersecting || oSphere.Intersects(oBufferPlane) == PlaneIntersectionType.Intersecting)
                    {
                        if (oMoveable.PrevPosition.Y < -PlaneLeft.D && oMoveable.PrevPosition.Y > -PlaneRight.D)
                        {
                            if (oSign == 1)
                            {
                                if (oDelta.X >= 0)
                                {
                                    oMoveable.VelocityX = -Math.Abs(oMoveable.VelocityX);
                                    if (oMoveable.PrevPosition.X < -PlaneBack.D)
                                    {
                                        var oPuck = oMoveable as Puck3D;
                                        if (oPuck != null)
                                        {
                                            oPuck.Position = new Vector3(oPuck.Position.X, 0, 20);
                                            oPuck.Velocity = Vector3.Normalize(-Vector3.UnitX + Vector3.UnitY) * oPuck.Velocity.Length() * 0.5f;
                                        }
                                        else
                                        {
                                            oMoveable.PositionX = -oBufferPlane.D - oMoveable.Radius;
                                            oMoveable.VelocityX *= 0.125f;
                                            oMoveable.VelocityY *= 0.125f;
                                        }
                                    }
                                }
                                else
                                {
                                    oMoveable.VelocityX = Math.Abs(oMoveable.VelocityX);
                                    if (oMoveable.PrevPosition.X >= -PlaneBack.D)
                                        oMoveable.PositionX = -PlaneBack.D + oMoveable.Radius;
                                }
                            }
                            else if (oSign == -1)
                            {
                                if (oDelta.X <= 0)
                                {
                                    oMoveable.VelocityX = Math.Abs(oMoveable.VelocityX);
                                    if (oMoveable.PrevPosition.X > -PlaneBack.D)
                                    {
                                        var oPuck = oMoveable as Puck3D;
                                        if (oPuck != null)
                                        {
                                            oPuck.Position = new Vector3(oPuck.Position.X, 0, 20);
                                            oPuck.Velocity = Vector3.Normalize(Vector3.UnitX + Vector3.UnitY) * oPuck.Velocity.Length() * 0.5f;
                                        }
                                        else
                                        {
                                            oMoveable.PositionX = -oBufferPlane.D + oMoveable.Radius;
                                            oMoveable.VelocityX *= 0.125f;
                                            oMoveable.VelocityY *= 0.125f;
                                        }
                                    }
                                }
                                else
                                {
                                    oMoveable.VelocityX = -Math.Abs(oMoveable.VelocityX);
                                    if (oMoveable.PrevPosition.X <= -PlaneBack.D)
                                        oMoveable.PositionX = -PlaneBack.D - oMoveable.Radius;
                                }
                            }
                        }
                    }

                }
                #endregion

            }
        }

        public override void Draw(GraphicsDevice oGraphics, Camera3D oCamera)
        {
            base.Draw(oGraphics, oCamera);

            if (DebugShapeRenderer.ShowLines)
            {
                DebugShapeRenderer.AddBoundingBox(ScoreArea, Color.Blue);
                DebugShapeRenderer.AddBoundingBox(CollisionArea, Color.Purple);

                DebugShapeRenderer.AddBoundingBox(PostLeft, Color.Green);
                DebugShapeRenderer.AddBoundingBox(PostRight, Color.Green);
                DebugShapeRenderer.AddBoundingBox(PostTop, Color.Green);
            }
        }

        public BoundingBox ScoreArea { get { return m_oScoreArea; } }
        public BoundingBox CollisionArea { get { return m_oCollisionArea; } }

        public Plane PlaneTop { get { return m_oTopPlane; } }
        public Plane PlaneBack { get { return m_oBackPlane; } }
        public Plane PlaneLeft { get { return m_oLeftPlane; } }
        public Plane PlaneRight { get { return m_oRightPlane; } }

        public BoundingBox PostLeft { get { return m_oLeftPost; } }
        public BoundingBox PostRight { get { return m_oRightPost; } }
        public BoundingBox PostTop { get { return m_oTopPost; } }

        private BoundingBox m_oScoreArea;
        private BoundingBox m_oCollisionArea;
        private Plane m_oTopPlane;
        private Plane m_oBackPlane;
        private Plane m_oLeftPlane;
        private Plane m_oRightPlane;
        private BoundingBox m_oLeftPost;
        private BoundingBox m_oRightPost;
        private BoundingBox m_oTopPost;

    }
}
